package com.gitee.wsl.compose.chart

//package io.github.koalaplot.core.util


import kotlin.math.PI

import kotlin.math.abs
import kotlin.math.asin
import kotlin.math.sqrt

/**
 * Converts the float receiver as a string with [precision] number of digits after the decimal
 * (rounded) (e.g. 35.72 with precision = 1 will be 35.7, 35.78 with precision = 2 will be 35.80)
 */
/*public fun Float.toString(precision: Int): String {
    return this.toDouble().toString(precision)
}*/

/**
 * Converts the number to a string with [precision] number of digits after the decimal (rounded)
 * (e.g. 35.72 with precision = 1 will be 35.7, 35.78 with precision = 2 will be 35.80)
 */
/*public fun Double.toString(precision: Int): String {
    val leftShifted = (round(abs(this) * 10.0.pow(precision))).toInt()
    val s = StringBuilder(leftShifted.toString())

    // left-pad with 0's to ensure enough digits
    for (x in (1..(precision + 1 - s.length))) {
        s.insert(0, "0")
    }

    // insert decimal point
    if (precision != 0) s.insert(s.lastIndex - (precision - 1), ".")

    // (re)insert negative sign
    if (this < 0) {
        s.insert(0, "-")
    }

    return s.toString()
}*/

/**
 * Seeks to find the maximum value, within the range of [min] and [max] and with an accuracy
 * of [tolerance], that results in a true result from the [eval] function.
 * [eval] must be a monotonically increasing function.
 * [min] must be a value that causes [eval] to return true, and max must be greater than [min].
 * [max] may be +infinity, but otherwise [min] and [max] must be numbers.
 */
internal fun maximize(
    min: Double,
    max: Double,
    tolerance: Double = 0.01,
    eval: (Double) -> Boolean
): Double {
    require(min <= max)
    require(tolerance > 0)
    require(min.isFinite())
    require(!max.isNaN())

    return maximizeUnguarded(min, max, tolerance, eval)
}

/**
 * This is the implementation of the recursive maximize function without the parameter checks
 * for performance purposes.
 */
private fun maximizeUnguarded(
    min: Double,
    max: Double,
    tolerance: Double = 0.01,
    eval: (Double) -> Boolean
): Double {
    if (abs((max - min) / ((max + min) / 2)) < tolerance || (max == 0.0 && min == 0.0)) {
        return min
    }

    val test = if (max.isInfinite()) {
        if (min == 0.0) {
            1.0
        } else {
            min * 2.0
        }
    } else {
        min + (max - min) / 2.0
    }

    return if (eval(test)) {
        maximizeUnguarded(test, max, tolerance, eval)
    } else {
        maximizeUnguarded(min, test, tolerance, eval)
    }
}


@RequiresOptIn(
    "This KoalaPlot API is experimental and is likely to change or to be removed in" +
            " the future."
)
annotation class ExperimentalKoalaPlotApi


internal fun min(vararg values: Float): Float {
    var m = Float.POSITIVE_INFINITY
    values.forEach {
        m = kotlin.math.min(m, it)
    }
    return m
}

internal fun max(vararg values: Float): Float {
    var m = Float.NEGATIVE_INFINITY
    values.forEach {
        m = kotlin.math.max(m, it)
    }
    return m
}

/**
 * Finds the distance between a point [p0] and a line defined by points [p1] and [p2], and
 * the point on the line that is closest to the point [p0].
 */
/*
internal fun lineDistance(p1: Vector, p2: Vector, p0: Vector): Pair<Float, Vector> {
    // Line unit vector
    val line: Vector = line(p1, p2)

    // get normal of the line
    val n = Vector(-line.values[1], line.values[0])

    val distance = abs((p0 - p1) * n) / n.norm()

    val scalar: Float = ((p0 - p1) * line)

    val point = p1 + scalar * line

    return Pair(distance, point)
}
*/

/*public sealed interface AngleUnit

@JvmInline
public value class Degrees(public val value: Double) : AngleUnit {
    public override fun toString(): String {
        return value.toString()
    }
}

@JvmInline
public value class Radians(public val value: Double) : AngleUnit {
    public override fun toString(): String {
        return value.toString()
    }
}

public fun Degrees.toRadians(): Radians = Radians(value * (DEG2RAD))

public fun Radians.toDegrees(): Degrees = Degrees(value / (DEG2RAD))

public fun AngleUnit.toRadians(): Radians {
    return when (this) {
        is Radians -> this
        is Degrees -> toRadians()
    }
}

public fun AngleUnit.toDegrees(): Degrees {
    return when (this) {
        is Radians -> toDegrees()
        is Degrees -> this
    }
}

public fun Float.toRadians(): Radians = Radians(this.toDouble())

public fun Double.toRadians(): Radians = Radians(this)

public fun Float.toDegrees(): Degrees = Degrees(this.toDouble())

public fun Double.toDegrees(): Degrees = Degrees(this)

public inline val Float.rad: Radians get() = Radians(this.toDouble())
public inline val Double.rad: Radians get() = Radians(this)

public inline val Float.deg: Degrees get() = Degrees(this.toDouble())
public inline val Double.deg: Degrees get() = Degrees(this)
public inline val Int.deg: Degrees get() = Degrees(this.toDouble())*/

/**
 * Polar to cartesian coordinate transformation.
 */
/*internal fun polarToCartesian(radius: Float, angle: Degrees): Offset = polarToCartesian(radius, angle.toRadians())

*//**
 * Polar to cartesian coordinate transformation.
 *//*
internal fun polarToCartesian(radius: Float, angle: Radians): Offset {
    return Offset((radius * cos(angle.value)).toFloat(), (radius * sin(angle.value)).toFloat())
}*/


/*
internal data class PolarCoordinate(val radius: Float, val angle: AngleUnit)
*/

/**
 * Cartesian to polar coordinate transformation.
 */
/*internal fun cartesianToPolar(offset: Offset): PolarCoordinate {
    val distance = offset.getDistance()
    return if (distance == 0f) {
        PolarCoordinate(0f, Radians(0.0))
    } else {
        // acos returns an angle between 0 and PI
        var theta = acos(offset.x / distance)
        if (offset.y < 0) {
            theta = -theta
        }
        PolarCoordinate(offset.getDistance(), Radians(theta.toDouble()))
    }
}*/

/**
 * Computes the 2 possible angles, in radians, that correspond to the provided y coordinate
 * and radius.
 */
internal fun y2theta(y: Float, radius: Float): Pair<Float, Float> {
    val theta = asin(y / radius)
    return Pair(theta, (PI - theta).toFloat())
}

/**
 * Returns the edge-length of a square circumscribed by a circle with the provided [diameter].
 */
internal fun circumscribedSquareSize(diameter: Double): Double {
    return diameter / sqrt(2.0)
}

